#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include "terminal.h"

#define SCORE_AREA_X 50
#define SCORE_AREA_Y 3

unsigned long long game_area[4][4];
unsigned long long score;
unsigned long long high_score;

int new_block(void) {
    int pro_next_x = 0;
    int pro_next_y = 0;
    int can_new_block = 0;
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            if (game_area[i][j] == 0) {
                can_new_block = 1;
                if (pro_next_x == 0 && pro_next_y == 0) {
                    pro_next_x = i;
                    pro_next_y = j;
                }
                srand((unsigned)time(NULL));
                if (rand() % 2 == 0) {
                    srand((unsigned)time(NULL));
                    if (rand() % 10 == 0) {
                        game_area[i][j] = 4;
                    } else {
                        game_area[i][j] = 2;
                    }
                    score++;
                    return 0;
                }
            }
        }
    }
    if (can_new_block) {
        srand((unsigned)time(NULL));
        if (rand() % 10 == 0) {
            game_area[pro_next_x][pro_next_y] = 4;
        } else {
            game_area[pro_next_x][pro_next_y] = 2;
        }
        score++;
        return 0;
    }
    return 1;
}

int move_right(void) {
    for (int i = 0; i < 4; i++) {
        int zero_pos = 3;
        for (int j = 2; j >= 0; j--) {
            if (game_area[i][zero_pos] == 0 && game_area[i][j] != 0) {
                game_area[i][zero_pos] = game_area[i][j];
                game_area[i][j] = 0;
                zero_pos--;
            } else if (game_area[i][j] == 0 && game_area[i][zero_pos] != 0) {
                zero_pos = j;
            }
        }
        for (int j = 1; j < 4; j++) {
           if (game_area[i][j] == game_area[i][j - 1]) {
                game_area[i][j] <<= 1;
                game_area[i][j - 1] = 0;
            }
        }
    }
    return new_block();
}

int move_left(void) {
    for (int i = 0; i < 4; i++) {
        int zero_pos = 0;
        for (int j = 1; j < 4; j++) {
            if (game_area[i][zero_pos] == 0 && game_area[i][j] != 0) {
                game_area[i][zero_pos] = game_area[i][j];
                game_area[i][j] = 0;
                zero_pos++;
            } else if (game_area[i][j] == 0 && game_area[i][zero_pos] != 0) {
                zero_pos = j;
            }
        }
        for (int j = 2; j >= 0; j--) {
           if (game_area[i][j] == game_area[i][j + 1]) {
                game_area[i][j] <<= 1;
                game_area[i][j + 1] = 0;
            }
        }
    }
    return new_block();
}


int move_down(void) {
    for (int i = 0; i < 4; i++) {
        int zero_pos = 3;
        for (int j = 2; j >= 0; j--) {
            if (game_area[zero_pos][i] == 0 && game_area[j][i] != 0) {
                game_area[zero_pos][i] = game_area[j][i];
                game_area[j][i] = 0;
                zero_pos--;
            } else if (game_area[j][i] == 0 && game_area[zero_pos][i] != 0) {
                zero_pos = j;
            }
        }
        for (int j = 1; j < 4; j++) {
           if (game_area[j][i] == game_area[j - 1][i]) {
                game_area[j][i] <<= 1;
                game_area[j - 1][i] = 0;
            }
        }
    }
    return new_block();
}

int move_up(void) {
    for (int i = 0; i < 4; i++) {
        int zero_pos = 0;
        for (int j = 1; j < 4; j++) {
            if (game_area[zero_pos][i] == 0 && game_area[j][i] != 0) {
                game_area[zero_pos][i] = game_area[j][i];
                game_area[j][i] = 0;
                zero_pos++;
            } else if (game_area[j][i] == 0 && game_area[zero_pos][i] != 0) {
                zero_pos = j;
            }
        }
        for (int j = 2; j >= 0; j--) {
           if (game_area[j][i] == game_area[j + 1][i]) {
                game_area[j][i] <<= 1;
                game_area[j + 1][i] = 0;
            }
        }
    }
    return new_block();
}

int main(void) {
    int ret;
    int errcode = 0;
    int input_status = 0;
    if (load_terminal_config() != 0) {
        return 1;
    }
    if (set_terminal_raw_mode() != 0) {
        errcode = 1;
        goto end;
    }
    FILE* score_fp;
    if (access("~/.2048_score", F_OK) == 0) {
        score_fp = fopen("~/.2048_score", "rt+");
    } else {
        score_fp = fopen("~/.2048_score", "wt");
    }
    if (score_fp == NULL) {
        perror("\e[91m无法打开计分文件~/.2048_score");
        printf("\e[0m");
        goto end;
    }
    int high_score_ch;
    while ((high_score_ch = fgetc(score_fp)) != EOF) {
    }
start:
    printf("\e[?25l\e[2J\e[0;0H");
    puts("\e[95m2048小游戏,使用方向键控制上下左右,按q退出游戏");
    printf("\e[%d;%dH得分: \e[96m%llu\n", SCORE_AREA_Y, SCORE_AREA_X - 6, score);
    new_block();
    new_block();
    score = 0;
    while (1) {
        printf("\e[%d;%dH\e[96m%llu\e[%d;%dH\n", SCORE_AREA_Y, SCORE_AREA_X, score, SCORE_AREA_Y + 2, 0);
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                printf("\e[93;104m%7llu", game_area[i][j]);
            }
            usleep(100);
            puts("\n");
        }
        printf("\e[0m");
        int ch;
        ret = read(STDIN_FILENO, &ch, 1);
        if (ret == 0) {
            break;
        }
        if (ret == -1) {
            perror("\e[91m无法读取输入");
            printf("\e[0m");
            errcode = 1;
            goto end;
        } else {
            switch (input_status) {
                case 0:
                    if (ch == 'q') {
                        goto end;
                    }
                    if (ch == '\e') {
                        input_status = 1;
                    }
                    break;
                case 1:
                    if (ch == 91) {
                        input_status = 2;
                    } else {
                        input_status = 0;
                    }
                    break;
                case 2:
                    switch (ch) {
                        case 65:  // 方向键上
                            if (move_up() != 0) {
                                goto game_over;
                            }
                            break;
                        case 66:  // 方向键下
                            if (move_down() != 0) {
                                goto game_over;
                            }
                            break;
                        case 67:  // 方向键右
                            if (move_right() != 0) {
                                goto game_over;
                            }
                            break;
                        case 68:  // 方向键左
                            if (move_left() != 0) {
                                goto game_over;
                            }
                    }
                    input_status = 0;
                    break;
            }
        }
    }
game_over:
    printf("\e[%d;%dH\e[91m\n游戏结束，您的得分是%llu，是否重新开始[y/n]，默认为n：", SCORE_AREA_Y + 4, SCORE_AREA_X, score);
    int ch = getchar();
    if (ch == 'y' || ch == 'Y') {
        score = 0;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                game_area[i][j] = 0;
            }
        }
        goto start;
    }
end:
    if (score_fp != NULL) {
        fclose(score_fp);
    }
    printf("\n\n\n\n\n\e[?25h\e[0m\n");
    ret = reset_terminal_config();
    if (ret != 0) {
        perror("\e[91m无法恢复终端初始环境");
        printf("\e[0m");
        errcode = 1;
    }
    return errcode;
}